/*
 * @Author: 锦阳
 * @Create: 2021年07月19日
 */
const exec = require('child_process').execSync;
const fetch = require('node-fetch');
const path = require('path');
const fs = require('fs-extra');
const FastFs = require('./FastFs');
const FastPath = require('./FastPath');
const compressing = require('compressing');
const CmdUtils = require('./CmdUtils');
const cache = {};
/**
 * npm工具集合
 *
 * @memberof module:nodeUtils
 */
class NpmUtils {
    /**
     * 获取当前项目的npm镜像地址
     *
     * @returns {string}
     */
    static getRegistry() {
        let registry;
        /* istanbul ignore next */
        if (!cache.registry) {
            try {
                registry = exec('npm config get registry');
            } catch (e) {}
            registry = registry && JSON.stringify(registry.toString().trim()).slice(1, -1);
            cache.registry = registry;
        }
        return cache.registry;
    }

    /* istanbul ignore next */
    /**
     * 获取npm包的所有版本的所有信息
     *
     * @param {string} packageName 包名
     * @param {string} [version] 版本或tag
     * @returns {Promise}
     */
    static getPackageInfo(packageName, version) {
        if (!cache.packages) {
            cache.packages = {};
        }
        if (!packageName) {
            return Promise.reject(new Error('[NpmUtils] getPackageInfo  缺少npm包名'));
        }
        const packageInfosUrl = `${packageName}${version ? '/' + version : ''}`;
        console.log(packageInfosUrl);
        if (!cache.packages[packageInfosUrl]) {
            cache.packages[packageInfosUrl] = fetch(`${this.getRegistry()}${packageInfosUrl}`)
                .then(
                    /* istanbul ignore next */
                    (res) => res.json(),
                )
                .catch(err => {
                    /* istanbul ignore next */
                    delete cache.packages[packageInfosUrl];
                    /* istanbul ignore next */
                    return Promise.reject(err);
                });
        }
        return cache.packages[packageInfosUrl];
    }

    /* istanbul ignore next */
    /**
     * 获取指定版本包信息
     *
     * @param {string} packageName 包名
     * @param {string} [version=latest] 版本或者版本tag
     * @returns {Promise} 对应版本包信息
     */
    static getPackageInfoWithVersion(packageName, version) {
        version = version || 'latest';
        return this.getPackageInfo(packageName, version);
    }

    /* istanbul ignore next */
    /**
     *下载npm包到本地
     *
     * @param {object} options 下载配置
     * @param {string} options.outputDir 输出目录
     * @param {string} options.packageName 包名
     * @param {string} [options.version] 版本或者版本tag
     * @param {boolean|InstallOption} [options.autoInstall] 自动下载npm包依赖或者自动下载配置
     * @returns {string} 包下载的本地路径
     */
    static async downloadPackage({ outputDir, packageName, version: versionOrTag, autoInstall }) {
        if (!outputDir) {
            throw new Error('[NpmUtils] 缺少必填参数 options.outputDir');
        }
        const packageInfo = await this.getPackageInfoWithVersion(packageName, versionOrTag);
        const version = packageInfo.version;
        let downloadUrl;
        try {
            downloadUrl = packageInfo.dist.tarball;
        } catch (error) {
            error.message = `[NpmUtils] 获取资源链接失败：[${packageName}][${version}]` + error.message;
            throw error;
        }
        const packageDir = FastPath.getCwdPath(outputDir, packageName, version);
        const packageFileDir = path.join(packageDir, 'package');
        if (!FastFs.getPathStatSync(packageFileDir)) {
            // 有缓存，直接读文件
            await fs.ensureDir(packageDir);
            const response = await fetch(downloadUrl);
            await compressing.tgz.uncompress(await response.buffer(), packageDir);
            if (autoInstall) {
                const packageFileDirPkg = path.join(packageFileDir, 'package.json');
                if (FastFs.getPathStatSync(packageFileDirPkg)) {
                    const dependencies = FastFs.readJsonSync(packageFileDirPkg).dependencies;
                    if (dependencies && Object.keys(dependencies).length) {
                        await CmdUtils.installDependencies(
                            {
                                cwd: packageFileDir,
                                stdio: 'ignore',
                                shell: true,
                            },
                            autoInstall.prod,
                        );
                    }
                }
            }
        }
        return packageFileDir;
    }
}

/**
 * 下载配置
 *
 * @typedef {object} InstallOption
 * @property {boolean} [prod=true] 是否只下载生产依赖
 * @memberof module:nodeUtils.NpmUtils.downloadPackage
 * @alias InstallOption
 */

module.exports = NpmUtils;
